/*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <sourcemod>
#include <sdktools>
#include <sdkhooks>

#include "macros.sp"
#include "helpers.sp"
#include "sqlhelpers.sp"

#define SOUND_NEW_VOTE_START	"ui/Beep_SynthTone01.wav"
#define SOUND_NEW_VOTE_WINNER	"ui/alert_clink.wav"

#define MAP_TYPE_FIRST 1
#define MAP_TYPE_NORMAL 2
#define MAP_TYPE_END 3
#define MAP_TYPE_FIRST_END 4

#define MAP_MAX_SIZE 32

new Handle:cvar_timevote = INVALID_HANDLE;
new Handle:cvar_timeresult = INVALID_HANDLE;


new voted[MAXCLIENTS] = { -1, ... };
new bool:can_vote = true;

new bool:on_change = false;
new bool:will_change = false;
new bool:official[3] = { true, true, true };
new winner;
new String:choosed_map[3][MAP_MAX_SIZE];
new String:choosed_campaign[3][MAP_MAX_SIZE];

//new Handle:hmenu_vote[MAXCLIENTS];

new Handle:timer_to_result;

new String:second_campaigns_name[13][32];
new String:second_maps_name[13][32];

public OnPluginStart()
{

	CreateConVar("rev_server_id", "1", "Server id from the database", CVAR_FLAGS);
	cvar_timevote = CreateConVar("l4d2_votemap_timevote", "40", "How many time before display vote", CVAR_FLAGS);
	cvar_timeresult = CreateConVar("l4d2_votemap_timeresult", "30", "How many time before vote is closed", CVAR_FLAGS);

	HookEvent("round_end", Event_OnRoundEnd, EventHookMode_Pre);
	HookEvent("round_start", Event_OnRoundStart, EventHookMode_Pre);

	strcopy(second_campaigns_name[0], 32, "Dead Center");
	strcopy(second_campaigns_name[1], 32, "Dark Carnival");
	strcopy(second_campaigns_name[2], 32, "Swamp Fever");
	strcopy(second_campaigns_name[3], 32, "Hard Rain");
	strcopy(second_campaigns_name[4], 32, "Parish");
	strcopy(second_campaigns_name[5], 32, "The Passing");
	strcopy(second_campaigns_name[6], 32, "The Sacrifice");
	strcopy(second_campaigns_name[7], 32, "No Mercy");
	strcopy(second_campaigns_name[8], 32, "Crash Course");
	strcopy(second_campaigns_name[9], 32, "Death Toll");
	strcopy(second_campaigns_name[10], 32, "Dead Air");
	strcopy(second_campaigns_name[11], 32, "Blood Harvest");
	strcopy(second_campaigns_name[12], 32, "Cold Stream");

	strcopy(second_maps_name[0], 32, "c1m1_hotel");
	strcopy(second_maps_name[1], 32, "c2m1_highway");
	strcopy(second_maps_name[2], 32, "c3m1_plankcountry");
	strcopy(second_maps_name[3], 32, "c4m1_milltown_a");
	strcopy(second_maps_name[4], 32, "c5m1_waterfront");
	strcopy(second_maps_name[5], 32, "c6m1_riverbank");
	strcopy(second_maps_name[6], 32, "c7m1_docks");
	strcopy(second_maps_name[7], 32, "c8m1_apartment");
	strcopy(second_maps_name[8], 32, "c9m1_alleys");
	strcopy(second_maps_name[9], 32, "c10m1_caves");
	strcopy(second_maps_name[10], 32, "c11m1_greenhouse");
	strcopy(second_maps_name[11], 32, "C12m1_hilltop");
	strcopy(second_maps_name[12], 32, "c13m1_alpinecreek");
}


public OnMapStart()
{
	//Precache sounds
	PrecacheSound(SOUND_NEW_VOTE_START);
	PrecacheSound(SOUND_NEW_VOTE_WINNER);

	if(is_first_round())
	{
		CreateTimer(5.0, timer_update_map, _, TIMER_FLAG_NO_MAPCHANGE);
	}
}

public Action:timer_update_map(Handle:timer, any:junk)
{
	decl String:strCurrentMap[MAP_MAX_SIZE];
	GetCurrentMap(strCurrentMap,MAP_MAX_SIZE);
	maps_choosing_get_map_info(strCurrentMap, result_map_info);
	
}

public result_map_info(Handle:owner, Handle:hndl, const String:error[], any:junk)
{
	IF_VALID_RESULT(0)
	{
		if(SQL_FetchRow(hndl)) /* check what kind of map */
		{
			new map_type = SQL_FetchInt(hndl, 0);
			new id_campaign = SQL_FetchInt(hndl, 1);
			process_info(map_type, id_campaign);
		}
	}	
}

public Action:display_next_map(Handle:timer, any:junk)
{
	EmitSoundToAll(SOUND_NEW_VOTE_WINNER);
	PrintToChatAll("\x01Next map is \x03%s", choosed_campaign[winner]);

	if(!official[winner])
	{
		display_subscribe();
	}	
}

public Event_OnRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(will_change && !is_first_round())
	{	
		CreateTimer(GetConVarFloat(cvar_timevote), display_next_map, _, TIMER_FLAG_NO_MAPCHANGE);	
	}

}
public Event_OnRoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(will_change && !is_first_round())
	{
		if(can_vote) //timer to result not expired yet
		{
			KillTimer(timer_to_result);
			calculate_next_map();		
		}

		if(!on_change)
		{		
			on_change = true;
			PrintToChatAll("\x01Changing to \x03%s\x01...", choosed_campaign[winner]);
			CreateTimer(12.0, change_map, _, TIMER_FLAG_NO_MAPCHANGE);
		}
	}
}

public Action:change_map(Handle:timer, any:junk)
{
	/* kick players to prevent auto kick + bugs from engine client */
	if(!official[winner])
	{
		FOR_EACH_HUMAN(client)
		{
			KickClient(client, "Download %s from Steam Workshop and rejoin 5.9.153.104:27015 REVOLUT10N", choosed_campaign[winner]);
		}
	}

	ServerCommand("changelevel %s", choosed_map[winner]);	//Change the campaign
}

process_info(map_type, id_campaign)
{
	/* last map */
	if(map_type == MAP_TYPE_END || map_type == MAP_TYPE_FIRST_END)
	{
		will_change = true;
		if(is_first_round()) //1st round
		{
			clear_vote();
			make_selection();
		}
	}
	/* first map */
	else if(map_type == MAP_TYPE_FIRST || map_type == MAP_TYPE_FIRST_END)
	{
		will_change = false;

		/* update database */
		if(is_first_round())
		{
			maps_choosing_update_campaign(id_campaign, GetConVarInt(FindConVar("rev_server_id")));	
		}
	}
	else
	{
		will_change = false;
	}
}


clear_vote()
{
	for(new i=0; i < MAXCLIENTS; i++)
	{
		voted[i] = -1;
	}
	
	can_vote = true;
	on_change = false;
}

public Action:call_vote(Handle:timer, any:junk)
{
	if(!can_vote)
		return;

	VoteMenuDraw();

	timer_to_result = CreateTimer(GetConVarFloat(cvar_timeresult), resultat, _, TIMER_FLAG_NO_MAPCHANGE);
}

make_selection()
{
	maps_choosing_select_random(GetConVarInt(FindConVar("rev_server_id")), result_map_random);

	CreateTimer(GetConVarFloat(cvar_timevote), call_vote, _, TIMER_FLAG_NO_MAPCHANGE);	
}

local_choose_except(num1, num2)
{
	new choose = GetRandomInt(0, 12);
	
	while(choose == num1 || choose == num2)
	{
		choose = (choose + 1) % 13
	}

	return choose;
}

get_num_of(const String:map_name[])
{
	for(new i=0; i < 13; i++)
	{
		if(strcmp(second_maps_name[i], map_name) == 0)
			return i;
	}

	return -1;
}
public result_map_random(Handle:owner, Handle:hndl, const String:error[], any:junk)
{
	IF_VALID_RESULT(0)
	{
		if(SQL_FetchRow(hndl)) /* check what kind of map */
		{
			decl String:camp[MAP_MAX_SIZE], String:map[MAP_MAX_SIZE];
			SQL_FetchString(hndl, 0, camp, sizeof(camp));
			SQL_FetchString(hndl, 1, map, sizeof(map));
			new id_rand = SQL_FetchInt(hndl, 2);
                        new camp_type = SQL_FetchInt(hndl, 3);

			strcopy(choosed_campaign[0], sizeof(camp), camp);
			strcopy(choosed_map[0], sizeof(map), map);
			
			if(camp_type == 1)
			{
				official[0] = true;
			}
			else
			{
				official[0] = false;
			}
			maps_choosing_select_older(GetConVarInt(FindConVar("rev_server_id")), id_rand, result_map_older);
			return;
		}
	}

	/* if fail or map not found **/
	new map1 = local_choose_except(-1, -1);
	new map2 = local_choose_except(map1, -1);
	new map3 = local_choose_except(map1, map2);

	strcopy(choosed_campaign[0], MAP_MAX_SIZE, second_campaigns_name[map1]);
	strcopy(choosed_map[0], MAP_MAX_SIZE, second_maps_name[map1]);
	strcopy(choosed_campaign[1], MAP_MAX_SIZE, second_campaigns_name[map2]);
	strcopy(choosed_map[1], MAP_MAX_SIZE, second_maps_name[map2]);
	strcopy(choosed_campaign[2], MAP_MAX_SIZE, second_campaigns_name[map3]);
	strcopy(choosed_map[2], MAP_MAX_SIZE, second_maps_name[map3]);

	official[0] = true;
	official[1] = true;
	official[2] = true;

}

public result_map_older(Handle:owner, Handle:hndl, const String:error[], any:id_rand)
{
	IF_VALID_RESULT(0)
	{
		if(SQL_FetchRow(hndl)) /* check what kind of map */
		{

			decl String:camp[MAP_MAX_SIZE], String:map[MAP_MAX_SIZE];
			SQL_FetchString(hndl, 0, camp, sizeof(camp));
			SQL_FetchString(hndl, 1, map, sizeof(map));

			new id_older = SQL_FetchInt(hndl, 2);
			new camp_type = SQL_FetchInt(hndl, 3);

			strcopy(choosed_campaign[1], sizeof(camp), camp);
			strcopy(choosed_map[1], sizeof(map), map);
			
			if(camp_type == 1)
                        {
                                official[1] = true;
                        }
                        else
                        {
                                official[1] = false;
                        }

			maps_choosing_select_less_played(GetConVarInt(FindConVar("rev_server_id")), id_older, id_rand, result_map_less_played);
			return;
		}
	}

	/* if fail or map not found */
	new map1 = get_num_of(choosed_map[0]);
	new map2 = local_choose_except(map1, -1);
	new map3 = local_choose_except(map1, map2);

	strcopy(choosed_campaign[1], MAP_MAX_SIZE, second_campaigns_name[map2]);
	strcopy(choosed_map[1], MAP_MAX_SIZE, second_maps_name[map2]);
	strcopy(choosed_campaign[2], MAP_MAX_SIZE, second_campaigns_name[map3]);
	strcopy(choosed_map[2], MAP_MAX_SIZE, second_maps_name[map3]);

	official[1] = true;
        official[2] = true;

}

public result_map_less_played(Handle:owner, Handle:hndl, const String:error[], any:junk)
{
	IF_VALID_RESULT(0)
	{
		if(SQL_FetchRow(hndl)) /* check what kind of map */
		{

			decl String:camp[MAP_MAX_SIZE], String:map[MAP_MAX_SIZE];
			SQL_FetchString(hndl, 0, camp, sizeof(camp));
			SQL_FetchString(hndl, 1, map, sizeof(map));
			new camp_type = SQL_FetchInt(hndl, 2);

			strcopy(choosed_campaign[2], sizeof(camp), camp);
			strcopy(choosed_map[2], sizeof(map), map);
			
			if(camp_type == 1)
                        {
                                official[2] = true;
                        }
                        else
                        {
                                official[2] = false;
                        }

			return;
		}
	}

	/* if fail or map not found */
	new map1 = get_num_of(choosed_map[0]);
	new map2 = get_num_of(choosed_map[1]);
	new map3 = local_choose_except(map1, map2);

	strcopy(choosed_campaign[2], MAP_MAX_SIZE, second_campaigns_name[map3]);
	strcopy(choosed_map[2], MAP_MAX_SIZE, second_maps_name[map3]);

        official[2] = true;
}


//Draw the menu for voting
public VoteMenuDraw()
{
	new Handle:panel = CreatePanel();
	SetPanelTitle(panel, "Vote for the next campaign :");	
	DrawPanelItem(panel, "I Don't Care");

	for(new i = 0; i < 3; i++)
	{
		DrawPanelItem(panel, choosed_campaign[i]);
	}
	

	FOR_EACH_HUMAN(client)
	{
		SendPanelToClient(panel, client, VoteMenuHandler, 30);	
		//Play a sound to indicate that the user can vote on a map
		EmitSoundToClient(client, SOUND_NEW_VOTE_START);
	}

	if(panel != INVALID_HANDLE)
		CloseHandle(panel);
}


//Handle the menu selection the client chose for voting
public VoteMenuHandler(Handle:hMenu, MenuAction:maAction, iClient, iItemNum)
{
	if(!IsValidHumanClient(iClient))
		return;

	if(!can_vote)
	{
		PrintHintText(iClient, "Sorry, vote is closed!");
		return;
	}
	if(maAction == MenuAction_Select) 
	{
		sub_debug("%N choosed %d", iClient, iItemNum);
		//Set the players current vote
		if(iItemNum == 1)
		{
			voted[iClient] = -1;
		}
		else
		{
			voted[iClient] = iItemNum - 2;
			PrintHintText(iClient, "You voted for %s", choosed_campaign[iItemNum - 2]);
		}				
	}

}

public Action:resultat(Handle:timer, any:junk)
{
	calculate_next_map();
}

calculate_next_map()
{
	can_vote = false;

	new result[3] = { 0, ... };
	
	/* count all votes */
	for(new i=1; i< MAXCLIENTS; i++)
	{
		if(voted[i] != -1)
		{
			result[voted[i]]++;
		}
	}

	/* who is the winner */
	winner = 0;
	new second = 1;
	new max = 0;
	new second_votes = 0;
	for(new i=0; i < 3; i++)
	{
		if(result[i] > max)
		{
			/*the old winner is now 2nd (if it's not the first - aka 0 - in this case we keep 1 in second)*/
			second = (i == 0 ? 1 : winner);
			second_votes = max;

			max = result[i];
			winner = i;
		}
		else
		{
			if(result[i] > second_votes)
			{
				second = i;
				second_votes = result[i];
			}
		}
	}

	EmitSoundToAll(SOUND_NEW_VOTE_WINNER);
	PrintToChatAll("\x01Le winning : \x03%s\x01! (%d votes)", choosed_campaign[winner], max);
	PrintToChatAll("\x01+-- Almost won : %s... (%d votes)", choosed_campaign[second], second_votes);

	if(!official[winner])
	{
		display_subscribe();
	}
}

display_subscribe()
{
	FOR_EACH_HUMAN(client)
	{
		PrintToChat(client, "\x01Subscribe to the map: \x03http://steamcommunity.com/sharedfiles/filedetails/?id=121455821\x01");
	}
}
